package com.indi.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.indi.common.constant.OrderConstant.OrderStatusEnum;
import com.indi.common.constant.WareConstant;
import com.indi.common.constant.WareConstant.LockStatusEnum;
import com.indi.common.dto.OrderDTO;
import com.indi.common.dto.WareLockStockDTO;
import com.indi.common.dto.mq.StockLockDTO;
import com.indi.common.dto.mq.StockLockDTO.StockLockDetailDTO;
import com.indi.common.enums.BizCodeEnum;
import com.indi.common.exception.Assert;
import com.indi.common.to.SkuStockTO;
import com.indi.common.utils.PageUtils;
import com.indi.common.utils.Query;
import com.indi.common.utils.R;
import com.indi.gulimall.ware.dao.WareSkuDao;
import com.indi.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.indi.gulimall.ware.entity.WareOrderTaskEntity;
import com.indi.gulimall.ware.entity.WareSkuEntity;
import com.indi.gulimall.ware.entity.result.WareSkuStockCountResult;
import com.indi.gulimall.ware.feign.OrderFeignService;
import com.indi.gulimall.ware.feign.ProductFeignService;
import com.indi.gulimall.ware.mapstruct.WareCovertBasic;
import com.indi.gulimall.ware.service.WareOrderTaskDetailService;
import com.indi.gulimall.ware.service.WareOrderTaskService;
import com.indi.gulimall.ware.service.WareSkuService;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {
    @Resource
    private ProductFeignService productFeignService;

    @Resource
    private WareSkuDao wareSkuDao;

    @Resource
    private WareOrderTaskService wareOrderTaskService;

    @Resource
    private WareOrderTaskDetailService wareOrderTaskDetailService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private OrderFeignService orderFeignService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        String wareId = (String) params.get("wareId");
        if (StringUtils.isNotEmpty(wareId)) {
            queryWrapper.eq("ware_id", wareId);
        }
        String skuId = (String) params.get("skuId");
        if (StringUtils.isNotEmpty(skuId)) {
            queryWrapper.eq("sku_id", skuId);
        }
        IPage<WareSkuEntity> page = this.page(new Query<WareSkuEntity>().getPage(params), queryWrapper);
        return new PageUtils(page);
    }

    @Override
    public void saveOrUpdateWareSku(Long skuId, Long wareId, Integer skuNum) {
        List<WareSkuEntity> warSkuList = this.list(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId)
                .eq("ware_id", wareId));
        if (warSkuList == null || warSkuList.size() == 0) {
            WareSkuEntity wareSku = new WareSkuEntity();
            wareSku.setSkuId(skuId);
            wareSku.setWareId(wareId);
            wareSku.setStock(skuNum);
            wareSku.setStockLocked(0);
            // 查询sku的名称
            try {
                R info = productFeignService.info(skuId);
                Map<String, Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
                if (info.getCode() == 0) {
                    wareSku.setSkuName((String) skuInfo.get("skuName"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.save(wareSku);
        } else {
            baseMapper.updateStock(skuId, skuNum, wareId);
        }
    }

    @Override
    public List<SkuStockTO> getStockBySkuIds(List<Long> skuIds) {
        List<WareSkuStockCountResult> wareSkuStockCountResult = baseMapper.getStockBySkuIds(skuIds);

        List<SkuStockTO> skuStockTOs = wareSkuStockCountResult.stream().map(item -> {
            SkuStockTO skuStockTO = new SkuStockTO();
            skuStockTO.setSkuId(item.getSkuId());
            skuStockTO.setStockOrNot(item.getStockCount() > 0);
            return skuStockTO;
        }).collect(Collectors.toList());
        return skuStockTOs;
    }

    @Transactional
    @Override
    public void lockStock(WareLockStockDTO wareLockStockDTO) {
        // 保存库存工作单，方便追溯源头
        WareOrderTaskEntity wareOrderTask = new WareOrderTaskEntity();
        wareOrderTask.setOrderSn(wareLockStockDTO.getOrderSn());
        wareOrderTaskService.save(wareOrderTask);

        // 遍历所有要购买的商品，查询库存中满足这些商品数量的所有仓库
        List<WareHasStock> wareHasStocks = wareLockStockDTO.getItems().stream().map(item -> {
            List<Long> wareIds = wareSkuDao.wareIdList(item.getSkuId(), item.getSkuQuantity());

            // 如果有一个商品查不出符合条件的仓库，就直接抛异常，下单失败
            Assert.notEmptyList(wareIds, BizCodeEnum.HAS_STOCK_EXCEPTION);

            WareHasStock wareHasStock = new WareHasStock();
            wareHasStock.setSkuId(item.getSkuId());
            wareHasStock.setWareIds(wareIds);
            wareHasStock.setSkuQuantity(item.getSkuQuantity());
            return wareHasStock;
        }).collect(Collectors.toList());

        // 走到这，就说明所有的商品都有库存，可以开始锁库存了
        for (WareHasStock item : wareHasStocks) {
            boolean stockLock = false; // 是否锁定成功

            for (Long wareId : item.getWareIds()) {
                int count = wareSkuDao.lockStock(item.getSkuId(), wareId, item.getSkuQuantity());
                /*
                    1、如果当前商品都锁定成功，就将当前商品锁定的工作单记录发给MQ
                    2、锁定失败。前面保存的工作单信息就回滚了。
                 */
                if (count == 1) {
                    // 保存库存工作单详情
                    saveWareOrderTaskDetail(wareOrderTask, item, wareId);

                    // 说明锁成功了，直接跳出循环，开始锁下一个商品
                    stockLock = true;
                    break;
                } else {
                    stockLock = false;
                }
            }

            // 这里判断stockLock，一旦为false，就说明当前商品在所有仓库中都锁定失败了，直接抛异常，下单失败
            Assert.isTrue(stockLock, BizCodeEnum.HAS_STOCK_EXCEPTION);
        }
    }

    @Transactional
    @Override
    public void unlockStock(StockLockDTO stockLockDTO) {
        // 库存工作单
        WareOrderTaskEntity wareOrderTask = wareOrderTaskService.getById(stockLockDTO.getId());
        if (wareOrderTask != null) {
            // 库存工作单详情
            WareOrderTaskDetailEntity orderTaskDetail = wareOrderTaskDetailService.getById(stockLockDTO
                    .getStockLockDetailDTO().getId());

            String orderSn = wareOrderTask.getOrderSn();
            R r = orderFeignService.infoByOrderSn(orderSn);
            // 远程服务调用失败
            Assert.isTrue(r.getCode() == 0, BizCodeEnum.FEIGN_SERVICE_EXCEPTION);
            // 订单信息
            OrderDTO orderDTO = r.getData(new TypeReference<OrderDTO>() {
            });
            // 查出的订单数据为空、订单状态已取消
            if (orderDTO == null || orderDTO.getStatus() == OrderStatusEnum.CANCELLED.getCode()) {
                // 库存工作单详情的状态为已锁定
                if (orderTaskDetail.getLockStatus() == LockStatusEnum.LOCKED.getCode()) {
                    finalUnlock(orderTaskDetail);
                }
            }

        }
    }

    @Transactional
    @Override
    public void unlockStock(OrderDTO orderDTO) {
        WareOrderTaskEntity task = wareOrderTaskService.infoByOrderSn(orderDTO.getOrderSn());
        // 根据库存工作单id查出所有已锁定的工作单详情
        List<WareOrderTaskDetailEntity> details = wareOrderTaskDetailService.lockedListByTaskId(task.getId());
        for (WareOrderTaskDetailEntity detail : details) {
            // 彻底解锁
            finalUnlock(detail);
        }
    }

    /**
     * 最终解锁
     *
     * @param orderTaskDetail
     */
    private void finalUnlock(WareOrderTaskDetailEntity orderTaskDetail) {
        // 解锁库存
        wareSkuDao.unlockStock(orderTaskDetail.getSkuId(), orderTaskDetail.getWareId(),
                orderTaskDetail.getSkuNum());
        // 将库存工作单详情的状态改为已解锁
        WareOrderTaskDetailEntity finalTaskDetail = new WareOrderTaskDetailEntity();
        finalTaskDetail.setLockStatus(LockStatusEnum.UNLOCKED.getCode());
        finalTaskDetail.setId(orderTaskDetail.getId());
        wareOrderTaskDetailService.updateById(finalTaskDetail);
    }

    /**
     * 保存库存工作单详情
     *
     * @param wareOrderTask
     * @param item
     * @param wareId
     */
    private void saveWareOrderTaskDetail(WareOrderTaskEntity wareOrderTask, WareHasStock item, Long wareId) {
        WareOrderTaskDetailEntity wareOrderTaskDetail = new WareOrderTaskDetailEntity();
        wareOrderTaskDetail.setSkuId(item.getSkuId());
        wareOrderTaskDetail.setSkuNum(item.getSkuQuantity());
        wareOrderTaskDetail.setTaskId(wareOrderTask.getId());
        wareOrderTaskDetail.setWareId(wareId);
        wareOrderTaskDetail.setLockStatus(1);
        wareOrderTaskDetailService.save(wareOrderTaskDetail);

        // 发消息保存库存工作单详情的数据，防止回滚之后没有数据
        StockLockDTO stockLockDTO = new StockLockDTO();
        stockLockDTO.setId(wareOrderTask.getId());

        StockLockDetailDTO stockLockDetailDTO = WareCovertBasic.INSTANCE
                .wareOrderTaskDetailToStockLockDetailDTO(wareOrderTaskDetail);
        stockLockDTO.setStockLockDetailDTO(stockLockDetailDTO);
        rabbitTemplate.convertAndSend(WareConstant.STOCK_EVENT_EXCHANGE, WareConstant.STOCK_LOCKED_ROUTING_KEY,
                stockLockDTO);
    }

    @Data
    class WareHasStock {
        private Long skuId;
        private List<Long> wareIds;
        private Integer skuQuantity;
    }
}